﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace spike.injection
{
    class Program
    {
        static void Main(string[] args)
        {
            var s = new Service();
            var smi = s.GetType().GetMethod("ProcessString");

            var c = new Client();
            var cei = c.GetType().GetEvent("OnOutput");

            Delegate csd = Delegate.CreateDelegate(typeof (Action<>).MakeGenericType(typeof (string)),
                                                   s,
                                                   smi,
                                                   false);

            //cei.AddEventHandler(c, csd);

            //c.OnOutput += new Intermediary(csd).PreProcessMessageWith("abc");
            //cei.AddEventHandler(c, new Intermediary(csd).PreProcessMessageWith(x => Console.WriteLine("pre: {0}", x)));
            //ConstructInterceptor(msg => csd.DynamicInvoke(msg))("kuckuck");

            //cei.AddEventHandler(c, ConstructInterceptor(msg => csd.DynamicInvoke(msg)));

            cei.AddEventHandler(c, Interceptor.Intercept(csd)
                                              .With(x => Console.WriteLine(":::{0}", x)));


            c.DoTheOutput("hello");
        }



        static void Dump(object o)
        {
            Console.WriteLine(o);
        }

        static void DumpString(string s)
        {}
    }



    class Interceptor
    {
        private Delegate messageProcessor;
        private Action<object> interception;


        public static Interceptor Intercept(Delegate messageProcessor)
        {
            return new Interceptor {messageProcessor = messageProcessor};                                    
        }


        public Delegate With(Action<object> interception)
        {
            this.interception = interception;

            return Delegate.CreateDelegate(typeof(Action<>).MakeGenericType(this.messageProcessor.Method.GetParameters()[0].ParameterType),
                                           this,
                                           this.GetType().GetMethod("ProcessMessage", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic),
                                           false);
        }


        void ProcessMessage(object message)
        {
            this.interception(message);
            this.messageProcessor.DynamicInvoke(message);
        }
    }



    class Client
    {
        public event Action<string> OnOutput;

        public void DoTheOutput(string text)
        {
            this.OnOutput(text);
        }
    }

    class Service
    {
        public void ProcessString(string text)
        {
            Console.WriteLine(text);
        }
    }
}
